class Entry < ActiveRecord::Base

  belongs_to :payee
  belongs_to :category
  belongs_to :account
  belongs_to :payee_mapping
  belongs_to :user

  composed_of :debit, :class_name => 'Money',
              :mapping => %w(debit cents) do |amount|
                amount.to_money
              end

  composed_of :credit, :class_name => 'Money',
              :mapping => %w(credit cents) do |amount|
                amount.to_money
              end

  attr_accessor :running_total, :payee_name

  validates_presence_of :payee_id, :if => Proc.new {|e| e.payee.nil? || !e.payee.new_record?}
  validates_associated :payee

  validates_presence_of :category_id
  validates_associated :category

  before_validation :set_new_payee
  before_save :save_new_payee

  def normalize!
    if mapping = find_payee_mapping
      self.payee = mapping.payee
      self.category = mapping.category
    end
  end

  def find_payee_mapping
    search = []
    bindings = {}

    search << "`match` = :match"
    bindings[:match] = original_payee

    search << "amount_lower >= :amount"
    search << "amount_upper <= :amount"
    bindings[:amount] = amount.cents

    PayeeMapping.find(:first, :conditions => [search.join(" AND "), bindings])
  end

  # The raw amount, i.e. a debit is negative.
  def amount
    Money.new(-debit.cents + credit.cents)
  end


  # validation methods

  def debit_and_credit_zero?
    credit.cents == 0 && debit.cents == 0
  end

  def debit_and_credit_nonzero?
    debit.cents > 0 && credit.cents > 0
  end

  def no_payees_defined?
    (!payee || !payee_id) && !payee_name
  end

  protected

  def validate
    errors.add_to_base("Debit or credit amount required.") if debit_and_credit_zero?
    errors.add_to_base("Debit and credit cannot both be set.") if debit_and_credit_nonzero?
    errors.add(:payee_name, ActiveRecord::Errors.default_error_messages[:blank]) if no_payees_defined?
  end

  def save_new_payee
    self.payee.save if self.payee.new_record?
  end

  def set_new_payee
    return if payee_name.blank?

    if !user.payees.exists?(:name => payee_name)
      new_payee = user.payees.build(:name => payee_name)
      self.payee = new_payee
    elsif !payee_name.blank? && payee_id.nil?
      self.payee = user.payees.find_by_name(payee_name)
    end
  end

end

module Enumerable
  def apply_running_total
    sum = Money.new(0)

    each do |entry|
      entry.running_total = entry.amount + sum
      sum += entry.amount
    end
  end
end
